寫了多年的 js ,遇到不熟悉的使用情境大家肯定會先 console.log
一下
曾經在 onChange event handler 中印過 event 是什麼嗎
function Input() {
const [value, setValue] = useState('');
function handleChange(e) {
console.log(e)
}
return <input value={value} onChange={handleChange}/>;
}
你會發現 console 中噴出一堆
Warning: This synthetic event is reused for performance reasons. If you're seeing this, you're accessing the property `xxx` on a released/nullified synthetic event. This is set to null. If you must keep the original synthetic event around, use event.persist(). See https://fb.me/react-event-pooling for more information.
這是因為 React 為了效能實作了 event pooling 這樣的機制
如同字面上的意思,Pooling 就是一個水池,裡面丟入很多也可以重複使用的 object,要用的時候直接抓出來,可以省去前面重複的 init 時間(constructor)。
至於為何能增進效能呢?我覺得 StackOverflow 上這個回答挺好的,大致翻譯一下就是
React 為了相容性,所以使用 SyntheticEvent 來包裝原生的 event
如果每次 fire event 時都要去生成一個新的 instance,勢必會造成大量的 GC,對效能影響頗大。所以使用 Pooling 的機制,讓 event 呼叫完後回到 pool 中,等待下次呼叫,就可以大幅降低 cpu time 了
所以相對應地, react 會在 evnet 收回時幫他做 nullify
, memeory 內所存入的 event attribute 也會跟著被回收掉,這也就是 console.log
時會報錯的原因
正因為上述的機制,我們無法利用 pass by reference 的方式去操作 async 的 event handler
那理所當然的就是改成使用「pass by value」囉!
這也正是為什麼 debounce event handler 只能傳進 value 或 event.target 的原因了
看完之後還是覺得很複雜嗎?其實不用擔心
正因為 ReactJS 的 event pooling 機制常讓使用者感到困惑,而且對於現代瀏覽器來說,效能的提升其實相對有限,所以在未來號稱沒有 breaking change 的 React 17 就把 event pooling 拔掉了,event.persist()
變成是預設的情境,直接呼叫也不會發生任何事了